package com.infoauto.opc.gateway.opc.factory;

import com.infoauto.opc.gateway.opc.model.INodeType;
import com.infoauto.opc.gateway.opc.model.MqttInterfaceNode;
import com.infoauto.opc.gateway.opc.model.SubscribeNode;
import com.infoauto.opc.gateway.opc.model.WebInterfaceNode;
import com.infoauto.opc.gateway.opc.utils.DataTypeConversionUtils;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * 订阅节点的工厂类
 * */
public class SubscribeNodeFactory {

    /// <summary>
    /// Manager实例
    /// </summary>
    private static SubscribeNodeFactory _manager;
    /// <summary>
    /// 锁定实例
    /// </summary>
    private static Object oClassLock = new Object();

    /// <summary>
    /// 得到该实例
    /// </summary>
    /// <returns></returns>
    public static SubscribeNodeFactory Instance()
    {
        synchronized (oClassLock) //加锁只生成一个实例
        {
            if (_manager == null)
            {
                _manager = new SubscribeNodeFactory();
            }
        }
        return _manager;
    }
    /// <summary>
    /// 私有的构造函数,防止从外部实例化
    /// </summary>
    private SubscribeNodeFactory()
    {
        mapSubscribeNodes = Collections.synchronizedMap(new HashMap<>());
        mapUnSubscribeNodes = Collections.synchronizedMap(new HashMap<>());
        mapWriteNodes = Collections.synchronizedMap(new HashMap<>());
        //imeCacheFacade = CacheUtils.getImeCache();
    }
    /// <summary>
    /// 得到该实例
    /// </summary>
    public static SubscribeNodeFactory This()
    {
        return Instance();
    }

    // 已经订阅的节点
    private Map<Integer, SubscribeNode> mapSubscribeNodes;


    // 轮循的节点
    private Map<Integer,SubscribeNode> mapUnSubscribeNodes;

    //下发的节点
    private Map<Integer,SubscribeNode> mapWriteNodes;

    //redis缓存
    //private ImeCacheFacade<CacheMetrics> imeCacheFacade;
    private final Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * 添加到订阅节点集合中
     * */
    public void addSubscriptNode(SubscribeNode sn,INodeType nodeType) {
        switch (nodeType)
        {
            case OPC_SUBSCRIBE:
                if(!this.mapSubscribeNodes.containsKey(sn.getNodeID()))
                    this.mapSubscribeNodes.put(sn.getNodeID(), sn);//订阅的节点集合赋值
                break;
            case OPC_READ:
                if(!this.mapUnSubscribeNodes.containsKey(sn.getNodeID()))
                    this.mapUnSubscribeNodes.put(sn.getNodeID(), sn);//轮询的节点集合赋值
                break;
            case OPC_WRITE:
                if(!this.mapWriteNodes.containsKey(sn.getNodeID()))
                    this.mapWriteNodes.put(sn.getNodeID(), sn);//下发的节点集合赋值
                break;
            case UNKNOW:

                break;
            default:
                break;
        }
    }

    /**
     *  读取节点
     *  nodeID 节点编号
     *  nodeType 节点类型
     * */
    public SubscribeNode getSubscriptNode(int nodeID,INodeType nodeType){
        SubscribeNode sn = null;
        switch (nodeType)
        {
            case OPC_SUBSCRIBE:
                sn = this.mapSubscribeNodes.get(nodeID);//TODO 有问题，应该订阅的序号才对
                break;
            case OPC_READ:
                sn = this.mapUnSubscribeNodes.get(nodeID);
                break;
            case OPC_WRITE:
                sn = this.mapWriteNodes.get(nodeID);
                break;
            default:
                break;
        }
        return sn;
    }

    /**
     * 根据服务端，读取轮循的节点集合
     * serverID OPC连接的编号
     * nodeType 节点类型
     * **/
    public List<SubscribeNode> getSubscribeNodesByServer(String serverID,INodeType nodeType){
        List<SubscribeNode> rst = new ArrayList<>();
        Map<Integer,SubscribeNode> mapRet = null;
        switch (nodeType)
        {
            case OPC_READ:
                mapRet = this.mapUnSubscribeNodes;
                break;
            case OPC_SUBSCRIBE:
                mapRet = this.mapSubscribeNodes;
                break;
            case OPC_WRITE:
                mapRet = this.mapWriteNodes;
                break;
            default:
                break;
        }
        if(mapRet!=null){
            for(SubscribeNode sn : mapRet.values()){
                if (sn.getServerID().equals(serverID)){
                    rst.add(sn);
                }
            }
        }
        return rst;
    }

    /**
     * 重置订阅节点的订阅状态
     * serverID OPC连接的编号
     * **/
    public void resetSubscribeNodeStatus(String serverID){
        for (SubscribeNode sn : this.mapSubscribeNodes.values()){
            if(sn.getOpcConnection().getConnectionID().equals(serverID)){
                sn.setSubscribe(false);
                sn.setMonitoredItem(null);
            }
        }
    }

    /***
     * 删除节点
     * */
    public void clearNodes(){
        if(this.mapUnSubscribeNodes!=null){
            this.mapUnSubscribeNodes.clear();
        }
        if(this.mapSubscribeNodes!=null){
            this.mapSubscribeNodes.clear();
        }
        if(this.mapWriteNodes!=null){
            this.mapWriteNodes.clear();
        }
    }

    /**
     * 匹配OPC节点和MQTT接口节点
     * */
    public void matchInterfaceNode(MqttInterfaceNode inode){
        SubscribeNode sn = this.mapSubscribeNodes.get(inode.getOpcNodeID());
        if(sn == null){
            sn = this.mapUnSubscribeNodes.get(inode.getOpcNodeID());
        }

        if(sn!=null){
            if(sn.getAssociatedINodes() == null){
                sn.setAssociatedINodes(Collections.synchronizedMap(new HashMap<>()));
            }
            sn.getAssociatedINodes().put(inode.getNodeID(),inode);
        }
    }

    /**
     * 匹配OPC节点和第三方接口节点
     * */
    public void matchWebInterfaceNode(WebInterfaceNode inode){
        SubscribeNode sn = this.mapWriteNodes.get(inode.getOpcNodeID());
        if(sn!=null){
            if(sn.getAssociatedINodes() == null){
                sn.setWebInterfaceNodes(Collections.synchronizedMap(new HashMap<>()));
            }
            //TODO 将opc点指向第三方接口，一个第三方接口只能指向一个OPC节点
            inode.setSubscribeNode(sn);
            // TODO 将第三方接口的属性绑定到OPC节点，一个OPC节点可以指向多个第三方接口
            sn.getWebInterfaceNodes().put(inode.getThirdNodeID(),inode);
        }
    }

    /**
     * 将值OPC节点及对应的值保存到Redis中
     * **/
//    public void saveNodes2Redis(SubscribeNode sn) {
//        try{
//            if(sn!=null && sn.getNodeValue()!=null && sn.getNodeValue().getValue()!=null){
//                String value = DataTypeConversionUtils.DataTypeConversionToString(sn.getNodeValue());
//                imeCacheFacade.set("sn."+sn.getNodeID(),value);
//            }else{
//                imeCacheFacade.set("sn."+sn.getNodeID(),"");
//            }
//        }catch (Exception e){
//            logger.error("saveNodes2Redis error = {} ",e.getMessage());
//        }
//    }

    /**
     * 下发值到PLC的
     * @param nodePath
     * @param value
     * @return
     */
    public Map<String,Object> writeValue(String nodePath,String value){
        nodePath = nodePath.toUpperCase();
        Map<String,Object> map = new HashMap<>();
        try{
            for(SubscribeNode sn : mapWriteNodes.values()){
                if(sn.getNodePath().toUpperCase().equals(nodePath)){
                    Variant variant = sn.readNodeValue(nodePath);
                    variant = DataTypeConversionUtils.DataTypeConversion(variant,value);
                    boolean b = sn.writeNodeValue(variant);
                    if(b){
                        map.put("nodePath",nodePath);
                        map.put("result","下发成功");
                    }else{
                        map.put("nodePath",nodePath);
                        map.put("result","下发失败");
                    }
                }
            }
            return map;
        }catch (Exception e){
            map.put("nodePath",nodePath);
            map.put("result","系统异常，下发失败");
            logger.error("writeValue error = {} ",e.getMessage());
        }
        return null;
    }

    /**
     * 读取PLC的值
     * @param nodePath
     * @return
     */
    public String readNodePathValue(String nodePath){
        nodePath = nodePath.toUpperCase();
        for(SubscribeNode sn : mapWriteNodes.values()){
            if(sn.getNodePath().toUpperCase().equals(nodePath)){
                String value = DataTypeConversionUtils.DataTypeConversionToString(sn.readNodeValue(nodePath));
                return value;
            }
        }
        return null;
    }



}
